package helexis.game.tyrian.domain;

import helexis.game.tyrian.Tyrian;
import helexis.game.tyrian.utils.TextUtils;

import java.util.HashMap;
import java.util.Map;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.utils.Json;
import com.badlogic.gdx.utils.Json.Serializable;
import com.badlogic.gdx.utils.OrderedMap;

/**
 * 사용자의 게임 정보를 포함하는 클래스
 * 
 * @author 조만희
 *
 */
public class Profile implements Serializable {
	
	
	/**
	 * 현재 레벨을 나타내는 ID
	 */
	private int currentLevelId;
	/**
	 * 먹은 돈...
	 */
	private int credits;
	/**
	 * 최고 점수
	 */
	private Map<Integer, Integer> highScores;
	/**
	 * 캐릭터 - 우주선
	 */
	private Ship ship;
	
	
	
	public Profile() {
		
		// 초기에 돈 1000 원 준다...
		this.credits = 1000;
		this.highScores = new HashMap<Integer, Integer>();
		
		// 기본 비행기 설정...
		this.ship = new Ship();
		this.ship.install(ShipModel.USP_TALON);
		this.ship.install(FrontGun.PULSE_CANNON);
		this.ship.install(Shield.SIF);
		
	}//end of constructor()
	
	
	
	public int getCurrentLevelId() {
		return currentLevelId;
	}
	
	public int getCredits() {
		return credits;
	}
	
	public Map<Integer, Integer> getHighScores() {
		return highScores;
	}
	
	public Ship getShip() {
		return ship;
	}
	
	
	public String getCreditsAsText() {
		return TextUtils.creditStyle(this.credits);
	}//end of getCreditsAsText()
	
	
	
	/**
	 * 주어진 레벨의 최고 점수를 리턴합니다.
	 * 
	 * @param levelId 레벨 ID
	 * @return 최고 점수
	 */
	public int getHighScore(int levelId) {
		
		// 레벨 정보 없으면, 0 점 !!
		if (this.highScores == null || !this.highScores.containsKey(levelId)) {
			return 0;
		}//end if
		
		return this.highScores.get(levelId);
		
	}//end of getHighScore()
	
	
	/**
	 * 주어진 레벨의 점수를 통지합니다.
	 * 
	 * 만약, 주어진 레벨의 최고 점수라면 값을 설정하고 true 를 리턴합니다.
	 * 
	 * @param levelId 레벨 ID
	 * @param score 점수
	 * @return 주어진 레벨의 최고 점수 여부
	 */
	public boolean notifyScore(int levelId, int score) {
		
		if (score > this.getHighScore(levelId)) {
			this.highScores.put(levelId, score);
			return true;
		}//end if
		
		
		return false;
		
	}//end of notifyScore()
	
	
	/**
	 * 주어진 아이템을 구매할 수 있는지 여부를 리턴합니다.
	 * 
	 * @param item 구매 대상 아이템
	 * @return 구매 가능 여부
	 */
	public boolean canBuy(Item item) {
		
		// 이미 아이템이 있거나, 가진 돈이 아이템보다 적으면 false 리턴.
		if (this.ship.contains(item) || item.getPrice() > this.credits) {
			return false;
		}//end if
		
		return true;
		
	}//end of canBuy()
	
	
	/**
	 * 주어진 아이템을 구매합니다.
	 * 
	 * 구매 가능하다면, 구매한 아이템을 우주선에 장착한 후 true 를 리턴합니다.
	 * 
	 * @param item 구매 대상 아이템
	 * @return 구매 여부
	 */
	public boolean buy(Item item) {
		
		if (this.canBuy(item)) {
			
			Gdx.app.log(Tyrian.LOG, "Buying item : " + item);
			this.ship.install(item);
			this.credits -= item.getPrice();
			Gdx.app.log(Tyrian.LOG, "Available credits : " + this.credits);
			return true;
			
		} else {
			
			Gdx.app.log(Tyrian.LOG, "No credits to buy item : " + item);
			return false;
			
		}//end if else
		
	}//end of buy()
	
	
	
	
	/*----------------------------------
	 * 
	 * Serialization 구현 부분...
	 * 
	 ----------------------------------*/
	
	
	private static final String KEY_CURRENT_LEVEL_ID = "currentLevelId";
	private static final String KEY_CREDITS = "credits";
	private static final String KEY_HIGH_SCORES = "highScores";
	private static final String KEY_SHIP = "ship";


	@Override
	public void write(Json json) {
		
		json.writeValue(KEY_CURRENT_LEVEL_ID, this.currentLevelId);
		json.writeValue(KEY_CREDITS, this.credits);
		json.writeValue(KEY_HIGH_SCORES, this.highScores);
		json.writeValue(KEY_SHIP, this.ship);

	}//end of write()

	@Override
	public void read(Json json, OrderedMap<String, Object> jsonData) {
		
		this.currentLevelId = json.readValue(KEY_CURRENT_LEVEL_ID, Integer.class, jsonData);
		this.credits = json.readValue(KEY_CREDITS, Integer.class, jsonData);
		
		/*
		 * libGDX 는 Map 형태의 데이터에 대해서 JSON 으로 로딩 시
		 * key 값은 무조건 String 으로 로딩하므로,
		 * 위에서 선언한 형태로 변경 작업이 필요함 !
		 */
		Map<String, Integer> highScores = json.readValue(KEY_HIGH_SCORES, HashMap.class, Integer.class, jsonData);
		for (String levelIdAsString : highScores.keySet()) {
			this.highScores.put(Integer.valueOf(levelIdAsString), highScores.get(levelIdAsString));
		}//end for
		
		this.ship = json.readValue(KEY_SHIP, Ship.class, jsonData);

	}//end of read()

}
//end of Profile.java